package xin.petstore.framework.web.img.dbmanager;

import xin.petstore.framework.web.img.Cm;
import xin.petstore.framework.web.img.domain.CodeImage;
import xin.petstore.framework.utils.ObjectUtils;
import com.mongodb.*;
import com.mongodb.gridfs.GridFS;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.gridfs.GridFSInputFile;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import java.lang.reflect.ParameterizedType;

/**
 * =============================
 * Author: 
 * Function: 
 * Version: 1.0
 * Date: 2016/7/4 0004
 * =============================
 */

public abstract class BaseMgRepository<T>  implements AbstractMgRepository<T> , InitializingBean {
    public static final Logger logger = LoggerFactory.getLogger(BaseMgRepository.class);
    private static final Random rand = new Random();
    private static DBCollection dbCollection;
    private static DB db;
    private static GridFS gridFS;
    private static Timer timer;
    private  Class<T> clazz;

    @Autowired
    private RedisTemplate jedisTemplate;
    @Autowired
    private MongoTemplate mongoTemplate;

    private static final  ThreadLocal<String> fileNames = new ThreadLocal<String>();//文件集合名
    
    public BaseMgRepository() {
        clazz = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }
	@Override
    public <T> T findOne(String key , String val ) {
        return  (T) mongoTemplate.findOne(new Query(Criteria.where(key).is(val)) , clazz);
    }

    @Override
    public <T> List<T> findAll() {
        return (List<T>) mongoTemplate.findAll(clazz);
    }

    @Override
    public <T> List<T> findByRegex(String key , String val) {
        Pattern pattern = Pattern.compile(val,Pattern.CASE_INSENSITIVE);
        Criteria criteria = new Criteria(key).regex(pattern.toString());
        return (List<T>)mongoTemplate.find(new Query(criteria), clazz);
    }

    @Override
    public void removeOne(String key , String id ) {
        Criteria criteria = Criteria.where(key).in(id);
        if(criteria == null){
            Query query = new Query(criteria);
            if(query != null && mongoTemplate.findOne(query, clazz) != null)
                mongoTemplate.remove(mongoTemplate.findOne(query, clazz));
        }
    }

    @Override
    public Object saveGridFSFile(File file , String fileName) {
        GridFSInputFile gfs = null;
        try {
            gfs = gridFS.createFile(file);
            if (StringUtils.isNotBlank(fileName)) {
                gfs.put(Cm.FILE_NAME, fileName);
            }
            gfs.save();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return gfs.getId();

    }
    
    @Override
    public Object saveGridFStream(InputStream file, String fileName) {
        GridFSInputFile gfs = null;
        gfs = gridFS.createFile(file);
        if (StringUtils.isNotBlank(fileName)) {
            gfs.put("fileName", fileName);
        }
        gfs.save();
        return gfs.getId();
    }
    @Override
    public void remGridFS(ObjectId _id) {
        gridFS.remove(_id);
    }
    @Override
    public void remGridFS(String  fileName) {
        gridFS.remove(fileName);
    }

    @Override
    public GridFSDBFile  getGridFS(ObjectId _id) {
            GridFSDBFile dbfile = gridFS.findOne(_id);
            return dbfile == null ? null:dbfile;
    }

    @Override
    public GridFSDBFile getGridFS(String fileName) {
        GridFSDBFile dbfile = gridFS.findOne(fileName);
        return dbfile == null ? null:dbfile;

    }

    @Override
    public <T> List distinct(String fielname ) {
        return mongoTemplate.getCollection(clazz.getSimpleName()).distinct(fielname);
    }

    @Override
    public Long count(String key , String val ) {
        return  mongoTemplate.count(new Query(Criteria.where(key).is(val)), clazz);
    }

    @Override
    public void insert(Object t) {
        mongoTemplate.insert(t);

    }

    @Override
    public List getRedisCacheList(String key) {
        List  list =   this.jedisTemplate.boundListOps(key).range(0L,
                this.jedisTemplate.boundListOps(key).size().longValue());
        if (CollectionUtils.isNotEmpty(list)) {
            return (List) list.get(0);
        }
        return  null;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Object o = fileNames.get();
        if (ObjectUtils.isNullOrEmpty(o)) {
        	fileNames.set("fs");
            o = "fs";
        }
        db = mongoTemplate.getDb();
        dbCollection = db.getCollection(o.toString());
        gridFS = new GridFS(db , o.toString());
        _init();
    }

    @Override
    public List getRandomList(int n){
        List list = new ArrayList();
        List redisCacheList = getRedisCacheList(Cm.REDIS_LIST);
        if (CollectionUtils.isEmpty(redisCacheList)) {
            InitRedisList();
        }
        while (CollectionUtils.isEmpty(redisCacheList)){
            Long starttime = System.currentTimeMillis();
            InitRedisList();
            redisCacheList = getRedisCacheList(Cm.REDIS_LIST);
            System.out.println("==>system reset RedisCacheList has finished! cost time:" +(System.currentTimeMillis()-starttime));
        }
        int[] randomCodeArray = getRandomCodeArray(1, redisCacheList.size(), n);
        for (int i = 0; i < randomCodeArray.length; i++) {
            list.add(redisCacheList.get(randomCodeArray[i]-1));
        }
        return list;
    }

    @Override
    public String getFileNameByOid(Object id ) {
        if (null == id ) return null;
        Query query  = new Query();
        query.addCriteria(new Criteria("originalId").is(id));
        CodeImage one = this.mongoTemplate.findOne(query, CodeImage.class);
        if(null == one) return null;
        return one.getFileName();
    }


    /**
     * 初始化方法
     * */
    private void InitRedisMap(){
        BoundHashOperations boundHashOperations = jedisTemplate.boundHashOps(Cm.REDIS_MAP);
        Map entries = boundHashOperations.entries();
        if (entries != null && entries.size() > 0 ) {
            return;
        }
        Map<String , Long> map = new HashMap<String , Long>();
        try {
            List fileNameList = distinct(Cm.CATEGORY_NAME);
            if (CollectionUtils.isNotEmpty(fileNameList)) {
                for (int i = 0; i < fileNameList.size() ; i++) {
                    String finame = fileNameList.get(i).toString();
                    Long count = count( Cm.CATEGORY_NAME , finame);
                    map.put(finame , count);
                }
            }

        }catch (Exception e){
            e.printStackTrace();
        }
        if (map != null && map.size() > 0 ) {
            boundHashOperations.putAll(map);
        }

    }


    /**
     * 初始化方法
     * */
    private void InitRedisList(){
        System.out.println("InitRedisList start");
        long start = System.currentTimeMillis();
        List range = this.jedisTemplate.boundListOps(Cm.REDIS_LIST).range(0L,
                this.jedisTemplate.boundListOps(Cm.REDIS_LIST).size().longValue());
        if (CollectionUtils.isNotEmpty(range) ) {
            return;
        }
        this.jedisTemplate.boundListOps(Cm.REDIS_LIST).leftPushAll(new Object[]{distinct(Cm.CATEGORY_NAME)});
        System.out.println("InitRedisList end , cost time: " + (System.currentTimeMillis()-start));

    }


    class TimerInit extends TimerTask {
        @Override
        public void run() {
            InitRedisList();
        }

    }
    private void _init(){
        timer = new Timer();
        timer.schedule(new TimerInit(), 0 , 12*60*60*1000);
    }


    public String getFileName() {
        return ObjectUtils.isNullOrEmpty(fileNames.get()) ? null:fileNames.get().toString();
    }

    public void setFileName(String fileName) {
        this.fileNames.set(fileName);
    }
	/**
     * @param min 指定范围最小值
     * @param max 指定范围最大值
     * @param n 随机数个数
     */
    public static int[] getRandomCodeArray(int min, int max, int n){
        if (n > (max - min + 1) || max < min) {
            return null;
        }
        int[] result = new int[n];
        AtomicInteger counter = new AtomicInteger(0);
        while(counter.intValue() < n) {
            int num = rand.nextInt(max)%(max-min+1)+min;
            boolean flag = true;
            for (int j = 0; j < n; j++) {
                if(num == result[j]){
                    flag = false;
                    break;
                }
            }
            if(flag){
                result[counter.intValue()] = num;
                counter.incrementAndGet();
            }
        }
        return result;
    }
    
    
    
	public static DBCollection getDbCollection() {
		return dbCollection;
	}
	public static DB getDb() {
		return db;
	}
	public static GridFS getGridFS() {
		return gridFS;
	}
	public RedisTemplate getJedisTemplate() {
		return jedisTemplate;
	}
	public MongoTemplate getMongoTemplate() {
		return mongoTemplate;
	}
	public static Random getRand() {
		return rand;
	}
    
    
}
